<!-- https://blog.csdn.net/weixin_57092157/article/details/130722805-->
<!-- https://www.tiny.cloud/docs/tinymce/6/available-toolbar-buttons/#code-plugin -->
<!-- https://www.tiny.cloud/docs/tinymce/5/content-filtering/#_control_characters_2 -->
<!-- 博客参考: https://blog.csdn.net/weixin_49523082/article/details/134973521 -->
<!-- 使用的tinymce 版本 5 -->
<template>
    <div>
        <Editor v-model="content" :init="init"></Editor>
    </div>
</template>

<script setup>
import {computed, onMounted, reactive, ref, watch} from 'vue'
import Editor from "@tinymce/tinymce-vue";
import crud from 'utils/crud'

//引入方式引入node_modules里的tinymce相关文件文件
import tinymce from 'tinymce/tinymce'; //tinymce默认hidden，不引入则不显示编辑器
import 'tinymce/themes/silver'; //编辑器主题，不引入则报错
import 'tinymce/icons/default'; //引入编辑器图标icon，不引入则不显示对应图标
import "tinymce/plugins/emoticons/js/emojis.js"; //必须引入这个文件才有表情图库
import "tinymce/models/dom/model";
//按需引入插件
import "tinymce/plugins/image"; // 图片
import "tinymce/plugins/table"; // 表格
import "tinymce/plugins/lists"; // 列表
import "tinymce/plugins/advlist"; //高级列表
import "tinymce/plugins/link";    // 链接
// import "tinymce/plugins/help";  //
import "tinymce/plugins/wordcount"; // 文字计数
import "tinymce/plugins/code";       // 编辑源码
import "tinymce/plugins/emoticons";  // 图标
import "tinymce/plugins/fullscreen"; // 全屏
import "tinymce/plugins/autolink"; //自动链接
import "tinymce/plugins/preview"; // 预览
import "tinymce/plugins/searchreplace"; // 查找替换
import "tinymce/plugins/quickbars"; // 光标处快捷提示, 配合 quickbars 使用
import "tinymce/plugins/insertdatetime"; //插入日期时间
import "tinymce/plugins/charmap"; // 特殊符号
import "./charmapExtend"; // 特殊符号
import "tinymce/plugins/save"; // 保存
// import "tinymce/plugins/autosave"; // 自动保存
// import "tinymce/plugins/autoresize"; // 自动调整编辑器大小 (偏向于固定大小, 内容太多自动大小太长)


const init = reactive({
    width: '100%',
    height: 500,
    language_url: "/tinymce/langs/zh-Hans.js", // 语言包路径
    language: 'zh-Hans',
    branding: false,
    // menubar: true,
    resize: false,
    paste_data_images: false,         // 允许粘贴图片上传,及格式 (主动监听 paste 粘贴, 避免无法获取文件名)
    image_dimensions: true,          // 图片宽高属性
    quickbars_insert_toolbar: "",     // 默认提示
    save_enablewhendirty: false,      // 此选项允许您禁用保存按钮，直到对编辑器的内容进行修改。默认情况下启用此选项。
    quickbars_selection_toolbar: "blocks bold italic underline strikethrough",      // 选中文字的快捷提示
    valid_elements: "*[*]", // 允许所有标签和元素
    // extended_valid_elements: "i[*]",
    keep_styles: true,
    // quickbars_image_toolbar: "alignleft aligncenter alignright",    // 选中图片的快捷提示
    skin_url: '/tinymce/skins/ui/oxide', // 皮肤：浅色
    // skin_url: '/tinymce/skins/ui/oxide-dark',//皮肤：暗色
    content_css: '/tinymce/skins/content/default/content.css', // 手动引入
    plugins: "lists advlist link image table wordcount code fullscreen emoticons preview autolink searchreplace quickbars   insertdatetime save charmap customSymbols",  // 插件
    toolbar_mode: 'sliding',  // wrap / sliding
    toolbar: "undo redo formatpainter" + // paste pastetext cut copy
        "| fullscreen" +
        "| formatselect blocks " +       // h1 h2 h3 h4 h5 h6
        " bold italic underline strikethrough " +
        "| numlist bullist advlist" +
        "| image media link table  charmap customSymbols insertdatetime emoticons " + // unlink
        "| fontfamily fontsize forecolor backcolor " +
        "| alignleft aligncenter alignright alignjustify outdent indent" +
        "| superscript subscript " +
        "| searchreplace directionality   removeformat" +
        "| wordcount preview code" +
        "| save ",
    //字体大小
    fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 48px 56px 72px',
    // 图片上传 (按钮弹出的上传组件) 直接返回 utl, 这里无法自动填入 alt
    images_upload_handler: (blobInfo) => new Promise(async (resolve, reject) => {
        let file = new File([blobInfo.blob()], blobInfo.filename());
        let data = await imgUpload(file, tinymce.activeEditor)
        resolve(data.url);
    }),
    // 添加图片的回调
    urlconverter_callback: function (url, node, on_save, name) {
        return url;
    },
    // 粘贴事件
    paste_postprocess: async function (editor) {
        console.log("paste_postprocess")
    },
    // 点击保存按钮 或 ctrl+s 快捷键可触发
    save_onsavecallback: () => {
        emits('save');
    },
    // 初始化 自定义操作
    init_instance_callback: function (editor) {
        // 自定义粘贴逻辑处理文件上传
        editor.on('paste', async function (e) {
            await pasteImgUpload(editor)
        })
    },
})


/**
 * 粘贴上传, 如果选择了多个文件, 会按文件名排序
 */
async function pasteImgUpload(editor) {
    // 获取剪切板内容
    let clipboardData = event.clipboardData;
    if (clipboardData.files == null || clipboardData.files.length === 0) {
        return
    }
    // 剪切板中的图片排序
    let files = Array.prototype.slice.call(clipboardData.files)
    // 获取原图, 需在 editor.execCommand 之前, 在后面获取不到剪切板数据
    let clipboardImgHtml = getClipboardImgHtml(clipboardData);
    // 上传文件
    for (let file of files) {
        let data = await imgUpload(file)
        // 判断是否存在原图，删除原图重新上传 (在其他网站复制时会出现两张图片的问题，一张是原始图片)
        editor.execCommand('mceInsertContent', false, `<p><img src="${data.url}" alt="${data.name}"></p>`);
    }
    // 判断是否存在原图, 存在删除需在 editor.execCommand 之后，否则获取不到光标位置
    //在 tinymce.js 中已存在 editor.on('paste', e => {  监听, 保存了原网站链接的图片, 这里需要删除它
    if (clipboardImgHtml !== "") {
        // 在 p 标签中把 p 一起消掉
        let newContent = editor.getContent().replace('<p>' + clipboardImgHtml + '</p>', "");
        // 如果不在 p 标签中
        newContent = newContent.replace(clipboardImgHtml, "");
        editor.setContent(newContent);
        // 手动触发编辑器的改变事件
        editor.fire('change');
    }
}

// 上传图片
async function imgUpload(file) {
    let res = await crud.upload(file);
    return res.data.data;
}


/**
 * 获取剪切板前的图片 IMG 信息html (原网站的 img 图片信息)
 * @param clipboardData
 * @returns {Promise<string>}
 */
function getClipboardImgHtml(clipboardData) {
    // 判断是否存在原始图片地址, 便于进行删除
    let clipboardContent = getDataTransferItems(clipboardData);
    let oldHtml = clipboardContent['text/html'];
    let oldImgHtml = "";
    if (oldHtml != null && oldHtml != '') {
        const element = new DOMParser().parseFromString(oldHtml, 'text/html');
        oldImgHtml = element.querySelector('img').outerHTML;
    }
    return oldImgHtml;
}

/**
 * 获取剪切板内容, 包括复制前的网站的图片链接的 网站信息
 * @param dataTransfer
 * @returns {{}}
 */
const getDataTransferItems = dataTransfer => {
    const items = {};
    if (dataTransfer && dataTransfer.types) {
        for (let i = 0; i < dataTransfer.types.length; i++) {
            const contentType = dataTransfer.types[i];
            try {
                items[contentType] = dataTransfer.getData(contentType);
            } catch (ex) {
                items[contentType] = '';
            }
        }
    }
    return items;
};


const props = defineProps({
    content: {type: String, default: ""},
});
// 初次加载-回显内容
const initContent = computed(() => {
    return props.content
});

const content = ref();
const emits = defineEmits(["update:content", "save"])

// 监听外部内容传入, 将至同步给当前编辑器
watch(initContent, (newVal) => {
    content.value = newVal;
}, {deep: true, immediate: true});

// 监听值变化, 将修改后的值同步给调用者
watch(content, (newVal) => {
    emits('update:content', newVal);
}, {deep: true});

</script>

<style>
/* 富文本按钮功能优先级, 最好 > 2500  弹出默认2000+ */
.tox-tinymce-aux {
    z-index: 9999 !important;
}

.tox-promotion {
    display: none;
}

</style>